using System;
using System.Collections.Generic;
using System.Data;
using NUnit.Framework;
using ODX.Core;
using ODX.LazyUnitTester;

namespace Presonia
{
    public enum Sex
    {
        Undefined,
        Male,
        Female
    }

    [Table]
    [HierarchyRoot]
    [TypeDef("P")]
    public abstract class Person : Entity
    {
        public abstract string Name { get; set; }
        public abstract Sex Sex{ get; set; }

        public abstract Person Mother { get; set; }
        public abstract Person Father{ get; set; }

        [Column("MotherID")]
        protected abstract IEntityList<Person> MotherChildren { get; }

        [Column("FatherID")]
        protected abstract IEntityList<Person> FatherChildren { get; }

        [Column("PairID")]
        protected abstract Person Pair { get; set; }

        [Column("FormerPairID")]
        public abstract IEntityList<Person> FormerPairs{ get; }

        public IEntityList<Person> Children { get { return Sex == Sex.Male ? FatherChildren : MotherChildren; } }

        [LinkTable("PersonToPerson", ParentColumn = "Friend1ID", ChildColumn = "Friend2ID")]
        public abstract IEntityList<Person> Friends { get; }

        [LinkTable("PersonToPerson", ParentColumn = "Friend2ID", ChildColumn = "Friend1ID")]
        protected abstract IEntityList<Person> Friends2 { get; }


        protected override void OnChanged(DataColumnChangeEventArgs e)
        {
            if (e.Column.ColumnName == "PairID")
            {
                foreach (Person p in new List<Person>(FormerPairs))
                    if (p == Pair)
                        FormerPairs.Remove(p);

                foreach (Person p in new List<Person>(FormerPairs))
                    if (p.Pair == this)
                        p.Pair = null;

                if (Pair != null && Pair.Pair != this)
                    Pair.Pair = this;

                foreach (Person p in new List<Person>(FormerPairs))
                    if (p == Pair)
                        FormerPairs.Remove(p);

            }
            base.OnChanged(e);
        }

        protected override void OnChanging(DataColumnChangeEventArgs e)
        {
            if (e.Column.ColumnName == "PairID")
                if (Pair != null)
                    FormerPairs.Add(Pair);

            base.OnChanging(e);
        }

        public void WritePair()
        {
            string pair = Pair != null ? Pair.Name : "null";
            Console.WriteLine("{0} -> {1}", Name, pair);
        }
    }

    [TypeDef("M")]
    public abstract class Man : Person
    {
        public Man() { Sex = Sex.Male; }
        public Woman Wife
        {
            get { return (Woman) Pair; }
            set { Pair = value; }
        }
    }

    [TypeDef("M")]
    public abstract class Woman : Person
    {
        public Woman() { Sex = Sex.Female; }
        public Man Husband
        {
            get { return (Man)Pair; }
            set { Pair = value; }
        }
    }

    [TestFixture]
    public class Program
    {
        [TestBody]
        static void Main()
        {
            Session s = new Session();
            s.RegisterAssembly(typeof(Person).Assembly);
            s.Prepare();

            Man john = s.Create<Man>();
            john.Name = "John";

            Man smith = s.Create<Man>();
            smith.Name = "Smith";

            Woman kate = s.Create<Woman>();
            kate.Name = "Kate";

            WriteFormers(kate);

            kate.Husband = smith;
            WriteFormers(kate);
            WriteFormers(smith);
            WriteFormers(john);
            
            kate.Husband = john;
            WriteFormers(kate);
            WriteFormers(smith);
            WriteFormers(john);

            kate.Husband = smith;
            WriteFormers(kate);
            WriteFormers(smith);
            WriteFormers(john);

            kate.Husband = john;
            WriteFormers(kate);
            WriteFormers(smith);
            WriteFormers(john);
        }

        private static void WriteFormers(Person person)
        {
            person.WritePair();
            foreach (Person fm in person.FormerPairs)
            {
                LUT.WriteLine(fm.Name);
            }
            LUT.WriteLine("----------------------------");
        }

        [Test]
        public void Test()
        {
            LUT.Execute(Main);
        }
    }
}
